
#ifndef __SOCKET_H__
#define __SOCKET_H__


#include <winsock.h>


namespace exl {
namespace net {


/**
 * IpAddress class
 * Tipo de dados para guardar um endereco IP
 */
class IpAddress
{
	void make_sock_address(unsigned char b[4])
	{
	}

public:
	/**
	 *
	 */
	struct sockaddr_in sock_address;
	DWORD ipaddress;
	union
	{
		unsigned char b[4];
		unsigned i;
	};


	/**
	 * Construtores
	 */
	IpAddress(DWORD ipAddress) : ipaddress(ipAddress)
	{
		char _serverHost[32];

		if (ipAddress)
		{
			unsigned char * ip = (unsigned char *) &ipAddress;
			sprintf(_serverHost, "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]);

			/* Construct address structure */
			memset(&sock_address, 0, sizeof(sock_address));           /* Zero out structure */
			sock_address.sin_family = AF_INET;                          /* Internet address family */

			hostent * host;
			if ((host = gethostbyname(_serverHost)) == 0)
			{
				sock_address.sin_addr.s_addr = inet_addr(_serverHost);
			}
			else
			{
				memcpy(&sock_address.sin_addr.s_addr, host->h_addr, host->h_length);
			}
		}
	}

	IpAddress(struct sockaddr_in ipAddress)
	{
		memcpy(&sock_address, &ipAddress, sizeof(sock_address));
	}

	IpAddress(unsigned char b[4])
	{
		this->b[0] = b[0];
		this->b[1] = b[1];
		this->b[2] = b[2];
		this->b[3] = b[3];
	}
};

class SocketBase
{
	friend class TcpServerSocket;
protected:
	enum Protocol { TCP, UDP };

	Protocol type;

	SocketBase(Protocol prot);

private:
	static void init();
	static bool started;
	
public:
	const static int Blocking = -1;
};


class UDPSocket : public SocketBase
{
public:
	static const u_long packetsize = 1400;

	UDPSocket() : SocketBase(UDP) { }
};


/**
 * TCP/IP
 */
class TcpSocket : public SocketBase
{
	friend class TcpServerSocket;
public:
	static const u_long packetsize = 4096;

protected:
	SOCKET socket;
	struct sockaddr_in remote;
	struct sockaddr_in local;

	virtual bool canRead(int timeout);

public:
	TcpSocket();
	virtual ~TcpSocket();

	virtual int send(void *, int);
	virtual int recv(void *, int, int timeout = Blocking);
	virtual bool connected();
	virtual void close();

	virtual int getRemotePort();
	virtual int getLocalPort();
	virtual void getRemoteIp(int ip[4]);

	virtual bool connect(const IpAddress &, int);
};



class TcpServerSocket : public SocketBase
{
private:
	bool listening;

	SOCKET socket;
	struct sockaddr_in server;

public:
	TcpServerSocket(int);
	virtual ~TcpServerSocket();

	virtual bool accept(TcpSocket *, int timeout = Blocking);
	virtual bool isListening() const { return listening; }
};



} }		// namespace exl::net

#endif

